﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Rotempco.ORM.Generator.Objects;
using Rotempco.ORM.Generator.Enums;
using System.IO;
using Rotempco.ORM.Generator.Templates;

namespace Rotempco.ORM.Generator.Logic
{
    public class TemplateFiller
    {
        public static string FillEnumTemplate(string enumNameSpace, string enumName, List<KeyValuePair<string, int?>> enumParameterList)
        {
            string enumTemplateContent = EnumTemplate.GetTemplate();  //File.ReadAllText(@"Templates\Enum.template");

            SetTemplateTag(ref enumTemplateContent, "@EnumNamespace", enumNameSpace);
            SetTemplateTag(ref enumTemplateContent, "@EnumName", enumName);

            StringBuilder stringBuilder = new StringBuilder();

            foreach (KeyValuePair<string, int?> pair in enumParameterList)
            {
                stringBuilder.Append(pair.Key);
                if (pair.Value.HasValue)
                {
                    stringBuilder.Append(" = ");
                    stringBuilder.Append(pair.Value.Value);
                }

                if (!pair.Equals(enumParameterList.LastOrDefault()))
                {
                    stringBuilder.Append(",");
                    stringBuilder.Append(Environment.NewLine);
                }
            }

            SetTemplateTag(ref enumTemplateContent, "@EnumParameters", stringBuilder.ToString());

            return enumTemplateContent;

        }

        public static string FillFactorySnippit(RotempcoSettings projectSettings, string domainClassName)
        {
            string factorySnippitContent = FactoryElementSnippit.GetTemplate();  // File.ReadAllText(@"Templates\FactoryElement.snippit");

            SetTemplateTag(ref factorySnippitContent, "@DomainClass", domainClassName);

            return factorySnippitContent;
        }

        public static string FillDataFactorySnippit(RotempcoSettings projectSettings, string domainClassName)
        {
            string factorySnippitContent = FactoryElementSnippit.GetTemplate();  // File.ReadAllText(@"Templates\FactoryElement.snippit");

            SetTemplateTag(ref factorySnippitContent, "@DomainClass", domainClassName);

            return factorySnippitContent;
        }

        public static string FillTemplate(RotempcoSettings projectSettings, TemplateEnum templateEnum, string tableName, string domainClassName, string aDALType, List<DatabaseColumn> databaseColumns)
        {
            string returnContent = "";  // File.ReadAllText(@"Templates\" + templateEnum.ToString() + ".template");

            switch (templateEnum)
            {
                case TemplateEnum.ADALCodebehind:
                    returnContent = ADALCodebehindTemplate.GetTemplate();
                    break;
                case TemplateEnum.DAL:
                    returnContent = DALTemplate.GetTemplate();
                    break;
                case TemplateEnum.EntityObject:
                    returnContent = EntityObjectTemplate.GetTemplate();
                    break;
                case TemplateEnum.PropertyEntityObject:
                    returnContent = PropertyEntityObjectTemplate.GetTemplate();
                    break;
                case TemplateEnum.Factory:
                    returnContent = FactoryTemplate.GetTemplate();
                    break;
                case TemplateEnum.DataFactory:
                    returnContent = DataFactoryTemplate.GetTemplate();
                    break;
                default:
                    break;
            }


            SetTemplateTag(ref returnContent, projectSettings, "@EntityObjectNameSpace");
            SetTemplateTag(ref returnContent, projectSettings, "@PropertyEntityObjectTemplateNameSpace");
            SetTemplateTag(ref returnContent, projectSettings, "@DALImplementationNamespace");
            SetTemplateTag(ref returnContent, projectSettings, "@ORMCoreObjects");
            SetTemplateTag(ref returnContent, projectSettings, "@ORMLogic");
            SetTemplateTag(ref returnContent, projectSettings, "@CoreLogic");
            SetTemplateTag(ref returnContent, projectSettings, "@ProjectName");

            SetTemplateTag(ref returnContent, "@TableName", tableName);
            SetTemplateTag(ref returnContent, "@DomainClass", domainClassName);
            SetTemplateTag(ref returnContent, "@ADALType", aDALType);

            DatabaseColumn primaryKeyColumn = (from d in databaseColumns where d.PrimaryKey == true select d).Single();
            SetTemplateTag(ref returnContent, "@PrimaryKey", primaryKeyColumn.Name);
            SetTemplateTag(ref returnContent, "@PrimaryType", FormatColumnTypes(primaryKeyColumn.ColumnType));

            SetTemplateTag(ref returnContent, "@DALORMMapping", GenerateDALORMMapping(domainClassName, databaseColumns));
            SetTemplateTag(ref returnContent, "@ParameterListAttributes", GenerateParameterListAttributes(databaseColumns));
            SetTemplateTag(ref returnContent, "@NullableParameterListAttributes", GenerateNullableParameterListAttributes(databaseColumns, withPreComma: false));
            SetTemplateTag(ref returnContent, "@WithPreCommaNullableParameterListAttributes", GenerateNullableParameterListAttributes(databaseColumns, withPreComma: true));
            SetTemplateTag(ref returnContent, "@ParameterListCall", GenerateParameterListCall(databaseColumns));
            SetTemplateTag(ref returnContent, "@NullableParameterListCall", GenerateNullableParameterListCall(databaseColumns));
            SetTemplateTag(ref returnContent, "@ParameterListMapping", GenerateParameterListMapping(domainClassName, databaseColumns));
            SetTemplateTag(ref returnContent, "@NullableParameterListMapping", GenerateNullableParameterListMapping(domainClassName, databaseColumns));
            SetTemplateTag(ref returnContent, "@Properties", GenerateProperties(databaseColumns));
            SetTemplateTag(ref returnContent, "@PropertyEntityName", GetPropertiesEntity(databaseColumns));
            SetTemplateTag(ref returnContent, "@EnumNamespaces", string.Join(Environment.NewLine, (from d in databaseColumns where d.IsEnum select "using " + d.EnumNamespace + ";")) + Environment.NewLine);
            SetTemplateTag(ref returnContent, "@SerializableNamespaces", string.Join(Environment.NewLine, (from d in databaseColumns where d.IsSerializable select "using " + d.SerializableNamespace + ";")) + Environment.NewLine);

            return returnContent;
        }

        private static string GetPropertiesEntity(List<DatabaseColumn> databaseColumns)
        {
            StringBuilder stringBuilder = new StringBuilder();
            foreach (DatabaseColumn databaseColumn in databaseColumns)
            {
                stringBuilder.Append("		");
                stringBuilder.Append("public static string ");

                stringBuilder.Append(databaseColumn.Name);
                stringBuilder.Append(" { get { return \"");
                stringBuilder.Append(databaseColumn.Name);
                stringBuilder.Append("\"; } }");

                if (databaseColumn != (databaseColumns.LastOrDefault()))
                    stringBuilder.Append(Environment.NewLine);
            }

            return stringBuilder.ToString();
        }

        private static string GenerateProperties(List<DatabaseColumn> databaseColumns)
        {
            StringBuilder stringBuilder = new StringBuilder();
            foreach (DatabaseColumn databaseColumn in databaseColumns)
            {
                stringBuilder.Append("		");
                stringBuilder.Append("public ");

                string columnTypeName = GetColumnType(databaseColumn);

                stringBuilder.Append(columnTypeName);

                if (databaseColumn.Nullable)
                {
                    if (databaseColumn.ColumnType != typeof(string) && databaseColumn.ColumnType != typeof(byte[]))
                        stringBuilder.Append("?");
                    else if (databaseColumn.EncryptionType != EncryptionTypeEnum.ClearText && columnTypeName != "string" && columnTypeName != "byte[]" && !databaseColumn.IsSerializable)
                        stringBuilder.Append("?");
                }
                stringBuilder.Append(" ");
                stringBuilder.Append(databaseColumn.Name);
                stringBuilder.Append(" { get; set; }");

                if (databaseColumn != (databaseColumns.LastOrDefault()))
                    stringBuilder.Append(Environment.NewLine);
            }

            return stringBuilder.ToString();
        }

        private static string GetColumnType(DatabaseColumn databaseColumn)
        {
            if (databaseColumn.IsEnum)
                return databaseColumn.EnumName;
            else if (databaseColumn.IsSerializable)
                return databaseColumn.SerializableName;
            else if (!string.IsNullOrWhiteSpace(databaseColumn.EncryptedDataType))
                return databaseColumn.EncryptedDataType;

            return FormatColumnTypes(databaseColumn.ColumnType);
        }

        private static string FormatColumnTypes(Type type)
        {
            if (type == typeof(Int16))
                return "short";
            else if (type == typeof(Int32))
                return "int";
            else if (type == typeof(Int64))
                return "long";
            else if (type == typeof(Double))
                return "double";
            else if (type == typeof(String))
                return "string";
            else if (type == typeof(Byte[]))
                return "byte[]";
            else if (type == typeof(Boolean))
                return "bool";

            return type.Name;
        }

        private static string GenerateParameterListMapping(string domainClassName, List<DatabaseColumn> databaseColumns)
        {
            StringBuilder stringBuilder = new StringBuilder();
            foreach (DatabaseColumn databaseColumn in databaseColumns)
            {
                stringBuilder.Append("			");
                stringBuilder.Append("mapping.Add(new PropertyPair(");
                stringBuilder.Append(LowerCaseFirstLetter(databaseColumn.Name));
                stringBuilder.Append(", SymbolExtensions.GetPropertyPair((");
                stringBuilder.Append(domainClassName);
                stringBuilder.Append(" d) => d.");
                stringBuilder.Append(databaseColumn.Name);
                stringBuilder.Append(")");
                stringBuilder.Append("));");
                stringBuilder.Append(Environment.NewLine);
            }

            return stringBuilder.ToString();
        }

        private static string GenerateNullableParameterListMapping(string domainClassName, List<DatabaseColumn> databaseColumns)
        {
            StringBuilder stringBuilder = new StringBuilder();
            foreach (DatabaseColumn databaseColumn in databaseColumns)
            {
                if (!databaseColumn.Nullable || databaseColumn.DeletedFlag)
                    continue;

                string nullablePropertyName = LowerCaseFirstLetter(databaseColumn.Name) + "IsNull";

                stringBuilder.Append("			");
                stringBuilder.Append("if (");
                stringBuilder.Append(nullablePropertyName);
                stringBuilder.Append(".HasValue)");
                stringBuilder.Append(Environment.NewLine);
                stringBuilder.Append("                nullablePairList.Add(new NullablePair(PropertyLogic.GetPropertyName((");
                stringBuilder.Append(domainClassName);
                stringBuilder.Append(" c) => c.");
                stringBuilder.Append(databaseColumn.Name);
                stringBuilder.Append("),");
                stringBuilder.Append(nullablePropertyName);
                stringBuilder.Append(".Value));");
                stringBuilder.Append(Environment.NewLine);
            }

            return stringBuilder.ToString();
        }

        private static string GenerateParameterListCall(List<DatabaseColumn> databaseColumns)
        {
            StringBuilder stringBuilder = new StringBuilder();

            foreach (DatabaseColumn databaseColumn in databaseColumns)
            {
                stringBuilder.Append(LowerCaseFirstLetter(databaseColumn.Name));
                stringBuilder.Append(": ");
                stringBuilder.Append(LowerCaseFirstLetter(databaseColumn.Name));

                if (databaseColumn != (databaseColumns.LastOrDefault()))
                    stringBuilder.Append(", ");
            }

            return stringBuilder.ToString();
        }

        private static string GenerateNullableParameterListCall(List<DatabaseColumn> databaseColumns)
        {
            StringBuilder stringBuilder = new StringBuilder();

            databaseColumns = (from d in databaseColumns where d.Nullable && !d.DeletedFlag select d).ToList();

            foreach (DatabaseColumn databaseColumn in databaseColumns)
            {
                stringBuilder.Append(LowerCaseFirstLetter(databaseColumn.Name) + "IsNull");
                stringBuilder.Append(": ");
                stringBuilder.Append(LowerCaseFirstLetter(databaseColumn.Name) + "IsNull");

                if (databaseColumn != (databaseColumns.LastOrDefault()))
                    stringBuilder.Append(", ");
            }

            return stringBuilder.ToString();
        }

        private static string GenerateParameterListAttributes(List<DatabaseColumn> databaseColumns)
        {
            StringBuilder stringBuilder = new StringBuilder();

            foreach (DatabaseColumn databaseColumn in databaseColumns)
            {
                string columnTypeName = GetColumnType(databaseColumn);
                if (databaseColumn.IsEnum)
                    stringBuilder.Append(databaseColumn.EnumName);
                else if (databaseColumn.IsSerializable)
                    stringBuilder.Append(databaseColumn.SerializableName);
                else
                    stringBuilder.Append(columnTypeName);

                // Is Nullable
                if (databaseColumn.ColumnType != typeof(string) && databaseColumn.ColumnType != typeof(byte[]))
                    stringBuilder.Append("?");
                else if (databaseColumn.EncryptionType != EncryptionTypeEnum.ClearText && columnTypeName != "string" && columnTypeName != "byte[]" && !databaseColumn.IsSerializable)
                    stringBuilder.Append("?");

                stringBuilder.Append(" ");

                stringBuilder.Append(LowerCaseFirstLetter(databaseColumn.Name));
                stringBuilder.Append(" = null");
                if (databaseColumn != (databaseColumns.LastOrDefault()))
                    stringBuilder.Append(", ");
            }

            return stringBuilder.ToString();
        }

        private static string GenerateNullableParameterListAttributes(List<DatabaseColumn> databaseColumns, bool withPreComma)
        {
            StringBuilder stringBuilder = new StringBuilder();


            databaseColumns = (from d in databaseColumns where d.Nullable && !d.DeletedFlag select d).ToList();

            if (withPreComma && databaseColumns.Count > 0)
                stringBuilder.Append(", ");

            foreach (DatabaseColumn databaseColumn in databaseColumns)
            {
                stringBuilder.Append("bool?");

                stringBuilder.Append(" ");

                stringBuilder.Append(LowerCaseFirstLetter(databaseColumn.Name));
                stringBuilder.Append("IsNull");
                stringBuilder.Append(" = null");

                if (databaseColumn != (databaseColumns.LastOrDefault()))
                    stringBuilder.Append(", ");
            }

            return stringBuilder.ToString();
        }
        private static string GenerateDALORMMapping(string domainClassName, List<DatabaseColumn> databaseColumns)
        {
            StringBuilder stringBuilder = new StringBuilder();
            foreach (DatabaseColumn databaseColumn in databaseColumns)
            {
                if (databaseColumn.PrimaryKey)
                    continue;

                stringBuilder.Append("			");
                stringBuilder.Append("mapping.Add(new ORMParameter(\"");
                stringBuilder.Append(databaseColumn.Name);
                stringBuilder.Append("\", SymbolExtensions.GetPropertyPair((");
                stringBuilder.Append(domainClassName);
                stringBuilder.Append(" d) => d.");
                stringBuilder.Append(databaseColumn.Name);
                stringBuilder.Append(")");

                if (databaseColumn.DeletedFlag)
                    stringBuilder.Append(", deletedProperty: true");

                if (databaseColumn.EncryptionType != EncryptionTypeEnum.ClearText)
                    stringBuilder.Append(", encryptionType: EncryptionTypeEnum." + databaseColumn.EncryptionType.ToString());

                if (databaseColumn.IsSerializable)
                    stringBuilder.Append(", serializableObject: true");

                stringBuilder.Append("));");
                stringBuilder.Append(Environment.NewLine);
            }

            return stringBuilder.ToString();
        }

        private static void SetTemplateTag(ref string templateContent, RotempcoSettings projectSettings, string tag)
        {
            SetTemplateTag(ref templateContent, tag, projectSettings.GetValue(tag));
        }

        private static void SetTemplateTag(ref  string templateContent, string tag, string tableName)
        {
            templateContent = templateContent.Replace(tag, tableName);
        }

        private static string LowerCaseFirstLetter(string str)
        {
            if (string.IsNullOrEmpty(str))
                return str;

            str = str[0].ToString().ToLower() + str.Substring(1);
            return str;
        }

    }
}
